如果有認真看前面文章的程式碼的話,應該會發現我大部分的範例,在實作抽象層都是用interface,但到實際案例時更多出現的是abstract class,但這是為什麼?
再繼續說下去前,我們可以先看看這兩個放在class會是甚麼樣的規則
interface的實作規則
abstract class的實作規則
如果以Microsoft Net. Framework Design Guidelines的建議來說,如果今天是要創造一個具有可重複利用能力的函式庫時(尤其是那些放在NuGet上的),這類函式庫其中一個特性就是在開發編譯時,並没有預設使用這份函式庫的使用者客群,所以要讓使用者擁有更多的修改彈性空間。
實際上來說,以放在NuGet上的Google Cloud API當作例子,這種會塞不少功能的都會是用abstract class去設計,而裡面的資料不是用virtual宣告,來讓使用者選擇要不要使用or延伸做法,就是用static表示此資料是唯一的
不過即使如此,也可以從這個案例中看出裡面的程式都是有很好的處理其中的權限與程式行數,防止其成為God class的可能性
SpeechClient.cs
namespace Google.Cloud.Speech.V1
{
public abstract class SpeechClient
{
protected SpeechClient();
public static ServiceMetadata ServiceMetadata { get; }
public static IReadOnlyList<string> DefaultScopes { get; }
public static string DefaultEndpoint { get; }
public virtual Speech.SpeechClient GrpcClient { get; }
public virtual OperationsClient LongRunningRecognizeOperationsClient { get; }
public static SpeechClient Create();
public static Task<SpeechClient> CreateAsync(CancellationToken cancellationToken = default);
public static Task ShutdownDefaultChannelsAsync();
有些很明確就是不想讓使用者擁有太多修改空間的還是會設計成interface,像是C#本身擁有IReadOnlyList案例
IReadOnlyList.cs
using System.Reflection;
namespace System.Collections.Generic
{
[DefaultMember("Item")]
public interface IReadOnlyList<out T> : IEnumerable<T>, IEnumerable, IReadOnlyCollection<T>
{
T this[int index] { get; }
}
}
所以實際上的使用還是要去評估此功能未來的擴充性去決定要用abstract還是interface才行,以目前來說,雖然我的目標是將棋類遊戲的功能模組化,但要真的能設計成那樣還需要一般功夫就是
依賴注入:原理、實作與設計模式
interface (C# 參考)
abstract (C# 參考)
Abstractions (Abstract Types and Interfaces)
IReadOnlyList 介面
C# IReadOnlyList用法